home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d11 / nansi.arc / NANSI.ASM < prev    next >
Assembly Source File  |  1987-06-27  |  29KB  |  1,092 lines

  1. ;--- nansi.asm ----------------------------------------------------------
  2. ; New ANSI terminal driver.
  3. ; Optimized for speed in the case of multi-character write requests.
  4. ; (C) 1986 Daniel Kegel, Pasadena, CA
  5. ; May be distributed for educational and personal use only
  6. ; The following files make up the driver:
  7. ;    nansi.asm   - all DOS function handlers except init
  8. ;    nansi_p.asm - parameter parser for ANSI escape sequences
  9. ;    nansi_f.asm - ANSI command handlers
  10. ;    nansi_i.asm - init DOS function handler
  11. ;
  12. ; Daniel Kegel, Bellevue, Washington & Pasadena, California
  13. ; Revision history:
  14. ; 5  july 85: brought up non-ANSI portion except forgot backspace
  15. ; 6  july 85: split off ANSI stuff into other files, added backspace
  16. ; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
  17. ; 12 july 85: fixed some scrolling bugs, began adding compaq flag
  18. ; 9  aug 85:  added cursor position reporting
  19. ; 10 aug 85:  added output character translation
  20. ; 11 aug 85:  added keyboard redefinition, some EGA 80x43 support
  21. ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
  22. ; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
  23. ; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
  24. ;          direct cursor positioning, takeover of BIOS write_tty,
  25. ;          noticed & squashed 2 related bugs in tab expansion
  26. ; 13 feb 86:  Squashed them again, harder
  27. ;------------------------------------------------------------------------
  28.  
  29.     include nansi_d.asm    ; definitions
  30.  
  31.     ; from nansi_f.asm
  32.     extrn    f_escape:near, f_in_escape:near
  33.  
  34.     ; from nansi_p.asm
  35.     extrn    param_end:word, redef_end:word
  36.  
  37.     ; from nansi_i.asm
  38.     extrn    dosfn0:near
  39.  
  40.     ; to nansi_p.asm
  41.     public    f_loopdone
  42.     public    f_not_ansi
  43.     public    f_ansi_exit
  44.  
  45.     ; to both nansi_p.asm and nansi_f.asm
  46.     public    cur_x, cur_y, max_x, cur_attrib
  47.  
  48.     ; to nansi_f.asm
  49.     public    xy_to_regs, get_blank_attrib
  50.     public    port_6845
  51.     public    wrap_flag
  52.     public    cur_parm_ptr
  53.     public    cur_coords, saved_coords, max_y
  54.     public    escvector, string_term
  55.     public    cpr_esc, cprseq
  56.     public    video_mode
  57.     public    lookup
  58.     public    in_g_mode
  59.  
  60.     ; to nansi_i.asm
  61.     public    req_ptr, break_handler
  62.     public    int_29
  63.     if    takeBIOS
  64.     public    new_vid_bios, old_vid_bios
  65.     endif
  66.  
  67.     ; to all modules
  68.     public    xlate_tab_ptr
  69.  
  70. ;--- seg_cs is the CS: override prefix
  71. ; (assembler forgets cs: on second "xlat dummy_cs_byte")
  72. seg_cs    macro
  73.     db    2eh
  74.     endm
  75.  
  76. ;--- push_all, pop_all ------------------------------------------------
  77. ; Save/restore all user registers.
  78. push_all    macro
  79.     if    is_8088
  80.     push    ax
  81.     push    bx
  82.     push    cx
  83.     push    dx
  84.     push    bp
  85.     push    si
  86.     push    di
  87.     else
  88.     pusha
  89.     endif
  90.     endm
  91.  
  92. pop_all macro
  93.     if    is_8088
  94.     pop    di
  95.     pop    si
  96.     pop    bp
  97.     pop    dx
  98.     pop    cx
  99.     pop    bx
  100.     pop    ax
  101.     else
  102.     popa
  103.     endif
  104.     endm
  105.  
  106. keybuf    struc                ; Used in getchar
  107. len    dw    ?
  108. adr    dw    ?
  109. keybuf    ends
  110.  
  111.  
  112. ABS40    segment at 40h
  113.     org    1ah
  114. buffer_head    dw    ?    ; Used in 'flush input buffer' dos call.
  115. buffer_tail    dw    ?
  116.  
  117.     org    49h
  118. crt_mode    db    ?
  119. crt_cols    dw    ?
  120. crt_len        dw    ?
  121. crt_start    dw    ?
  122. cursor_posn    dw    8 dup (?)
  123. cursor_mode    dw    ?
  124. active_page    db    ?
  125. addr_6845    dw    ?
  126. crt_mode_set    db    ?    ; = 7 only if monochrome display adaptor
  127. crt_palette    db    ?
  128.     org    6ch
  129. timer_low    dw    ?    ; low word of time-of-day counter (18.2 hz)
  130.  
  131. ABS40    ends
  132.  
  133.     page
  134.  
  135. CODE    segment byte public 'CODE'
  136. assume    cs:code, ds:code
  137.  
  138.     ; Device Driver Header
  139.  
  140.     org    0
  141.  
  142.     dd    -1            ; next device
  143.     dw    8013h            ; attributes
  144.     dw    strategy        ; request header pointer entry
  145.     dw    interrupt        ; request entry point
  146.     db    'CON'                ; device name (8 char)
  147.     db    5 dup (20h)             ; Fix to device name above
  148.  
  149.     ; Identification- in case somebody TYPEs the assembled driver
  150.     db    27, '[2J'
  151.     db    "Nansi.sys v2.2"
  152.     ife    is_8088
  153.     db    "(80286)"
  154.     endif
  155.     db    ': New ANSI driver (C) Daniel Kegel, Pasadena, CA 1986'
  156.     db    13, 10, 26
  157.  
  158.  
  159. ;----- variable area --------------------
  160. req_ptr label    dword
  161. req_off dw    ?
  162. req_seg dw    ?
  163.  
  164. wrap_flag    db    1    ; 0 = no wrap past line end
  165. escvector    dw    0    ; state vector of ESCape sequencor
  166. video_mode    db    3    ; ROM BIOS video mode (2=BW, 3=color)
  167. max_y        db    24
  168. max_cur_x    label    word    ; used to get both max & cur at once
  169. max_x        db    79    ; line width (79 for 80x25 modes)
  170. cur_coords    label    word
  171. cur_x        db    0    ; cursor position (0 = left edge)
  172. cur_y        db    0    ;          (0 = top edge)
  173. saved_coords    dw    ?    ; holds XY after a SCP escape sequence
  174. string_term    db    0    ; either escape or double quote
  175. cur_attrib    db    7    ; current char attributes
  176. cur_page    db    0    ; current display page
  177. video_seg    dw    ?    ; segment of video card
  178. f_cptr_seg    dw    ?    ; part of fastout write buffer pointer
  179. cur_parm_ptr    dw    ?    ; last byte of parm area now used
  180. port_6845    dw    ?    ; port address of 6845 card
  181. xlate_tab_ptr    dw    ?    ; pointer to output translation table
  182.         if    takeBIOS
  183. old_vid_bios    dd    ?    ; pointer to old video bios routine
  184.         endif
  185.  
  186. brkkeybuf    db    3    ; control C
  187. fnkeybuf    db    ?    ; holds second byte of fn key codes
  188. cpr_buf        db    8 dup (?), '['
  189. cpr_esc        db    1bh    ; descending buffer for cpr function
  190.  
  191. ; following four keybufs hold information about input
  192. ; Storage order determines priority- since the characters making up a function
  193. ; key code must never be separated (say, by a Control-Break), they have the
  194. ; highest priority, and so on.    Keyboard keys (except ctrl-break) have the
  195. ; lowest priority.
  196.  
  197. fnkey    keybuf    <0, fnkeybuf>    ; fn key string (0 followed by scan code)
  198. cprseq    keybuf    <0>        ; CPR string (ESC [ y;x R)
  199. brkkey    keybuf    <0, brkkeybuf>    ; ^C
  200. xlatseq keybuf    <0>        ; keyboard reassignment string
  201.  
  202. ;------ xy_to_regs --------------------------------------------
  203. ; on entry: x in cur_x, y in cur_y
  204. ; on exit:  dx = chars left on line, di = address
  205. ; Alters ax, bx.
  206. xy_to_regs    proc    near
  207.     ; Find number of chars 'till end of line, keep in DX
  208.     mov    ax, max_cur_x
  209.     mov    bx, ax            ; save max_x & cur_x for next block
  210.     mov    ah, 0            ; ax = max_x
  211.     xchg    dx, ax
  212.     mov    al, bh
  213.     mov    ah, 0            ; ax = cur_x
  214.     sub    dx, ax
  215.     inc    dx            ; dx is # of chars till EOL
  216.     ; Calculate DI = current address in text buffer
  217.     mov    al, bl            ; al = max_x
  218.     inc    al
  219.     mul    cur_y
  220.     add    al, bh            ; al += cur_x
  221.     adc    ah, 0            ; AX is # of chars into buffer
  222.     add    ax, ax
  223.     xchg    di, ax            ; DI is now offset of cursor.
  224.     ret
  225. xy_to_regs    endp
  226.  
  227.  
  228. ;------- dos_fn_tab -------------
  229. ; This table is used in "interrupt" to call the routine that handles
  230. ; the requested function.
  231.  
  232. max_cmd equ    12
  233. dos_fn_tab:
  234.     dw    dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  235.     dw    dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  236.  
  237. ;------- strategy ----------------------------------------------------
  238. ; DOS calls strategy with a request which is to be executed later.
  239. ; Strategy just saves the request.
  240.  
  241. strategy    proc    far
  242.     mov    cs:req_off,BX
  243.     mov    cs:req_seg,ES
  244.     ret
  245. strategy    endp
  246.  
  247. ;------ interrupt -----------------------------------------------------
  248. ; This is where the request handed us during "strategy" is
  249. ; actually carried out.
  250. ; Calls one of 12 subroutines depending on the function requested.
  251. ; Each subroutine returns with exit status in AX.
  252.  
  253. interrupt    proc    far
  254.     sti
  255.     push_all            ; preserve caller's registers
  256.     push    ds
  257.     push    es
  258.  
  259.     ; Read requested function information into registers
  260.     lds    bx,cs:req_ptr
  261.     mov    al,[BX+02h]        ; al = function code
  262.     les    si,[BX+0Eh]        ; ES:SI = input/output buffer addr
  263.     mov    cx,[BX+12h]        ; cx = input/output byte count
  264.  
  265.     cmp    al, max_cmd
  266.     ja    unk_command        ; too big, exit with error code
  267.  
  268.     xchg    bx, ax
  269.     shl    bx, 1            ; form index to table of words
  270.     mov    ax, cs
  271.     mov    ds, ax
  272.     call    word ptr dos_fn_tab[bx]
  273. int_done:
  274.     lds    bx,cs:req_ptr        ; report status
  275.     or    ax, 100h        ; (always set done bit upon exit)
  276.     mov    [bx+03],ax
  277.  
  278.     pop    ES            ; restore caller's registers
  279.     pop    DS
  280.     pop_all
  281.     ret                ; return to DOS.
  282.  
  283. unk_command:
  284.     call    badcmd
  285.     jmp    int_done
  286.  
  287. interrupt    endp
  288.  
  289. ;----- BIOS break handler -----------------------------------------
  290. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  291. ; Simply notes that a break was hit.  Flag is checked during input calls.
  292.  
  293. break_handler    proc
  294.     mov    cs:brkkey.len, 1
  295.     iret
  296. break_handler    endp
  297.  
  298.     page
  299.  
  300. ;------ badcmd -------------------------------------------------------
  301. ; Invalid function request by DOS.
  302. badcmd    proc    near
  303.     mov    ax, 813h        ; return "Error: invalid cmd"
  304.     ret
  305. badcmd    endp
  306.  
  307.  
  308. ;------ nopcmd -------------------------------------------------------
  309. ; Unimplemented or dummy function request by DOS.
  310. nopcmd    proc    near
  311.     xor    ax, ax            ; No error, not busy.
  312.     ret
  313. nopcmd    endp
  314.  
  315. ;------- dos function #4 ----------------------------------------
  316. ; Reads CX characters from the keyboard, places them in buffer at
  317. ; ES:SI.
  318. dosfn4    proc    near
  319.     jcxz    dos4done
  320.     mov    di, si
  321. dos4lp: push    cx
  322.     call    getchar
  323.     pop    cx
  324.     stosb
  325.     loop    dos4lp
  326. dos4done:
  327.     xor    ax, ax            ; No error, not busy.
  328.     ret
  329. dosfn4    endp
  330.  
  331. ;-------- dos function #5: non-destructive input, no wait ------
  332. ; One-character lookahead into the keyboard buffer.
  333. ; If no characters in buffer, return BUSY; otherwise, get value of first
  334. ; character of buffer, stuff into request header, return DONE.
  335. dosfn5    proc    near
  336.     call    peekchar
  337.     jz    dos5_busy
  338.  
  339.     lds    bx,req_ptr
  340.     mov    [bx+0Dh], al
  341.     xor    ax, ax            ; No error, not busy.
  342.     jmp    short dos5_exit
  343. dos5_busy:
  344.     MOV    ax, 200h        ; No error, busy.
  345. dos5_exit:
  346.     ret
  347.  
  348. dosfn5    endp
  349.  
  350. ;-------- dos function #6: input status --------------------------
  351. ; Returns "busy" if no characters waiting to be read.
  352. dosfn6    proc    near
  353.     call    peekchar
  354.     mov    ax, 200h        ; No error, busy.
  355.     jz    dos6_exit
  356.     xor    ax, ax            ; No error, not busy.
  357. dos6_exit:
  358.     ret
  359. dosfn6    endp
  360.  
  361. ;-------- dos function #7: flush input buffer --------------------
  362. ; Clears the IBM keyboard input buffer.  Since it is a circular
  363. ; queue, we can do this without knowing the beginning and end
  364. ; of the buffer; all we need to do is set the tail of the queue
  365. ; equal to the head (as if we had read the entire queue contents).
  366. ; Also resets all the device driver's stuffahead buffers.
  367. dosfn7    proc    near
  368.     xor    ax, ax
  369.     mov    fnkey.len, ax        ; Reset the stuffahead buffers.
  370.     mov    cprseq.len, ax
  371.     mov    brkkey.len, ax
  372.     mov    xlatseq.len, ax
  373.  
  374.     mov    ax, abs40
  375.     mov    es, ax
  376.     mov    ax, es:buffer_head    ; clear queue by making the tail
  377.     mov    es:buffer_tail, ax    ; equal to the head
  378.  
  379.     xor    ax, ax            ; no error, not busy.
  380.     ret
  381. dosfn7    endp
  382.  
  383.     page
  384.     if    takeBIOS
  385. ;--- new_vid_bios -------------------------------------------
  386. ; New_vid_bios simply replaces the write_tty call.
  387. ; All other calls get sent to the old video bios.
  388. ; This gives BIOS ANSI capability.
  389. ; However, it takes away the escape character.
  390. ; If this is not desired, just tell init to not take over the vector.
  391.  
  392. new_vid_bios    proc
  393.     cmp    ah, 14
  394.     jz    nvb_write_tty
  395.     jmp    dword ptr cs:old_vid_bios
  396. nvb_write_tty:
  397.     push    cx
  398.     mov    cl, cs:cur_attrib
  399.     ; If in graphics mode, BL is new color
  400.     call    in_g_mode        ; returns carry set if text mode
  401.     jc    nvb_wt_text
  402.         mov    cs:cur_attrib, bl    ; ja?
  403. nvb_wt_text:
  404.     int    29h            ; write AL
  405.     mov    cs:cur_attrib, cl    ; restore color
  406.     pop    cx
  407.     iret
  408.  
  409. new_vid_bios    endp
  410.     endif
  411.  
  412. ;------ int_29 ----------------------------------------------
  413. ; Int 29 handles DOS quick-access putchar.
  414. ; Last device loaded with attribute bit 4 set gets accessed for
  415. ; single-character writes via int 29h instead of via interrupt.
  416. ; Must preserve all registers.
  417. ; Installed as int 29h by dosfn0 (init).
  418. int_29_buf    db    ?
  419.  
  420. int_29    proc    near
  421.     sti
  422.     push    ds
  423.     push    es
  424.     push_all
  425.     mov    cx, 1
  426.     mov    bx, cs
  427.     mov    es, bx
  428.     mov    ds, bx
  429.     mov    si, offset int_29_buf
  430.     mov    byte ptr [si], al
  431.     call    dosfn8
  432.     pop_all
  433.     pop    es
  434.     pop    ds
  435.     iret
  436. int_29    endp
  437.  
  438.     page
  439. ;------ dosfn8 -------------------------------------------------------
  440. ; Handles writes to the device (with or without verify).
  441. ; Called with
  442. ;  CX     = number of bytes to write
  443. ;  ES:SI = transfer buffer
  444. ;  DS     = CS, so we can access local variables.
  445.  
  446. dosfn8    proc    near
  447.  
  448.     mov    f_cptr_seg, es    ; save segment of char ptr
  449.  
  450.     ; Read the BIOS buffer address/cursor position variables.
  451.     mov    ax, abs40
  452.     mov    ds, ax
  453.     assume    ds:abs40
  454.  
  455.     ; Find current video mode and screen size.
  456.     mov    ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  457.     mov    cs:video_mode, al
  458.     dec    ah            ; ah = max column
  459.     mov    cs:max_x, ah
  460.  
  461.     ; Find current cursor coordinates.
  462.     mov    al,active_page
  463.     cbw
  464.     add    ax,ax
  465.     xchg    bx,ax
  466.     mov    ax,cursor_posn[bx]
  467.     mov    cs:cur_coords,AX
  468.  
  469.     ; Find video buffer segment address; adjust it
  470.     ; so the offset is zero; return in AX.
  471.  
  472.     ; DS is abs40.
  473.     ; Find 6845 address.
  474.     mov    ax, addr_6845
  475.     mov    cs:port_6845, ax
  476.     ; Find video buffer address.
  477.     MOV    AX,crt_start
  478.     shr    ax, 1
  479.     shr    ax, 1
  480.     shr    ax, 1
  481.     shr    ax, 1
  482.     add    ah, 0B0h        ; assume it's a monochrome card...
  483.     CMP    cs:video_mode,07
  484.     jz    d8_gots
  485.     add    ah, 8            ; but if not mode 7, it's color.
  486. d8_gots:
  487.     push    cs
  488.     pop    ds
  489.     assume    ds:code
  490.     mov    video_seg, ax
  491.     mov    es, ax
  492.     call    xy_to_regs        ; Set DX, DI according to cur_coords.
  493.  
  494.     ; | If in graphics mode, clear old pseudocursor
  495.     call    in_g_mode
  496.     jc    d8_no_cp
  497.         call    pseudocursor    ; write block in xor
  498. d8_no_cp:
  499.     mov    bx, xlate_tab_ptr    ; get pointer to translation table
  500.  
  501.     mov    ah, cur_attrib
  502.     mov    ds, f_cptr_seg        ; get segment of char ptr
  503.     assume    ds:nothing
  504.     cld                ; make sure we'll increment
  505.  
  506.     ; The Inner Loop: 4+12 +4+4 +4+4 +0+15 +4+4 +4+18 = 77 cycles/loop
  507.     ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
  508.     ; At that speed, it takes 32 milliseconds to fill a screen.
  509.  
  510.     ; Get a character, put it on the screen, repeat 'til end of line
  511.     ; or no more characters.
  512.     jcxz    f_loopdone        ; if count = 0, we're already done.
  513.     cmp    cs:escvector, 0        ; If in middle of an escape sequence,
  514.     jnz    f_in_escapex        ; jump to escape sequence handler.
  515.  
  516. f_tloop:; | If in graphics mode, jump to alternate loop
  517.     ; | What a massive kludge!  A better approach would have been
  518.     ; | to collect characters for a "write n chars" routine
  519.     ; | which would handle both text and graphics modes.
  520.     call    in_g_mode
  521.     jc    f_t_cloop
  522.     jmp    f_g_cloop
  523.  
  524. f_t_cloop:
  525.     LODSB                ; get char! (al = ds:[si++])
  526.     cmp    al, 28            ; is it a control char?
  527.     jb    f_control        ;  maybe...
  528. f_t_nctl:
  529.     seg_cs
  530.     xlat
  531.     STOSW                ; Put Char! (es:[di++] = ax)
  532.     dec    dx            ; count down to end of line
  533.     loopnz    f_t_cloop        ; and go back for more.
  534.     jz    f_t_at_eol        ; at end of line; maybe do a crlf.
  535.     jmp    short f_loopdone
  536.  
  537. f_looploop:
  538. f_ansi_exit:                ; in case we switched into
  539.     loopnz    f_tloop            ; a graphics mode
  540. f_t_at_eol:
  541.     jz    f_at_eol
  542.  
  543. f_loopdone:
  544.  
  545.     ;--------- All done with write request -----------
  546.     ; DI is cursor address; cursor position in cur_y, dl.
  547.     mov    ax, cs
  548.     mov    ds, ax            ; get our segment back
  549.     assume    ds:code
  550.  
  551.     ; Restore cur_x = max_x - dx + 1.
  552.     mov    al, max_x
  553.     inc    al
  554.     sub    al, dl
  555.     mov    cur_x, al
  556.     ; Set cursor position; cursor adr in DI; cursor pos in cur_x,cur_y
  557.     call    set_pseudocursor
  558.     ; Return to DOS.
  559.     xor    ax, ax            ; No error, not busy.
  560.     ret
  561.  
  562.     ;---- handle control characters ----
  563.     ; Note: cur_x is not kept updated in memory, but can be
  564.     ; computed from max_x and dx.
  565.     ; Cur_y is kept updated in memory.
  566. f_control:
  567.     cmp    al, 27            ; Is it an escape?
  568.     jz    f_escapex
  569.     cmp    al, 13            ; carriage return?
  570.     jz    f_cr
  571.     cmp    al, 10            ; line feed?
  572.     jz    f_lf
  573.     cmp    al, 9            ; tab?
  574.     jz    f_tabx
  575.     cmp    al, 8            ; backspace?
  576.     jz    f_bs
  577.     cmp    al, 7            ; bell?
  578.     jz    f_bell
  579.     jmp    f_nctl            ; then it is not a control char.
  580.  
  581. f_tabx: jmp    f_tab
  582. f_escapex:
  583.     jmp    f_escape
  584. f_in_escapex:
  585.     jmp    f_in_escape
  586.  
  587. f_bs:    ;----- Handle backspace -----------------
  588.     ; Moves cursor back one space without erasing.    No wraparound.
  589.     cmp    dl, cs:max_x        ; wrap around to previous line?
  590.     ja    fbs_wrap        ; yep; disallow it.
  591.     dec    di            ; back up one char & attrib,
  592.     dec    di
  593.     inc    dx            ; and note one more char left on line.
  594. fbs_wrap:
  595.     jmp    f_looploop
  596.  
  597. f_bell: ;----- Handle bell ----------------------
  598.     ; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
  599.     call    beep
  600.     or    al, al            ; clear z
  601. ; old (more portable) version:
  602. ;    mov    ax, 0e07h        ; "write bell to tty simulator"
  603. ;    mov    bx, 0            ; "page zero, color black"
  604. ;    int    10h            ; call BIOS
  605. ;    mov    ah, cs:cur_attrib    ; restore current attribute
  606. ;    mov    bx, cs:xlate_tab_ptr    ; restore translate table address
  607. ;    or    al, al            ; al still 7; this clears Z.
  608.     jmp    f_looploop        ; Let main loop decrement cx.
  609.  
  610. f_cr:    ;----- Handle carriage return -----------
  611.     ; di -= cur_x<<1;        set di= address of start of line
  612.     ; dx=max_x+1;            set bx= chars left in line
  613.     mov    al, cs:max_x
  614.     inc    al
  615.     sub    al, dl            ; Get cur_x into ax.
  616.     mov    ah, 0
  617.     sub    di, ax
  618.     sub    di, ax
  619.     mov    dl, cs:max_x        ; Full line ahead of us.
  620.     inc    dx
  621.     mov    ah, cs:cur_attrib    ; restore current attribute
  622.     or    al, 1            ; clear z
  623.     jmp    f_looploop        ; and let main loop decrement cx
  624.  
  625. f_at_eol:
  626.     ;----- Handle overrunning right end of screen -------
  627.     ; cx++;                compensate for double loop
  628.     ; if (!wrap_flag) { dx++; di-=2; }
  629.     ; else do_crlf;
  630.     inc    cx
  631.     test    cs:wrap_flag, 1
  632.     jnz    feol_wrap
  633.         dec    di
  634.         dec    di
  635.         inc    dx
  636.         jmp    f_looploop
  637. feol_wrap:
  638.     ; dx=max_x+1;            set bx= chars left in line
  639.     ; di -= 2*(max_x+1);
  640.     ; do_lf
  641.     mov    dl, cs:max_x
  642.     inc    dx
  643.     sub    di, dx
  644.     sub    di, dx
  645.     ; fall thru to line feed routine
  646.  
  647. f_lf:    ;----- Handle line feed -----------------
  648.     ; if (cur_y >= max_y) scroll;        scroll screen up if needed
  649.     ; else { cur_y++; di += max_x<<1;    else increment Y
  650.  
  651.     mov    al, cs:max_y
  652.     cmp    cs:cur_y, al
  653.     jb    flf_noscroll
  654.         call    scroll_up        ; preserves bx,cx,dx,si,di
  655.         jmp    short flf_done
  656. flf_noscroll:
  657.     inc    cs:cur_y
  658.     mov    al, cs:max_x
  659.     mov    ah, 0
  660.     inc    ax
  661.     add    ax, ax
  662.     add    di, ax
  663. flf_done:
  664.     mov    ah, cs:cur_attrib        ; restore current attribute
  665.     or    al, 1            ; clear z
  666.     jmp    f_looploop        ; and let main loop decrement cx
  667.  
  668. f_tab:    ;----- Handle tab expansion -------------
  669.     ; Get cur_x into al.
  670.     mov    al, cs:max_x
  671.     inc    al
  672.     sub    al, dl
  673.     ; Calculate number of spaces to output.
  674.     push    cx            ; save cx
  675.     mov    ch, 0
  676.     mov    cl, al            ; get zero based x coordinate
  677.     and    cl, 7
  678.     neg    cl
  679.     add    cl, 8            ; 0 -> 8, 1 -> 8, ... 7 -> 1
  680.     sub    dx, cx            ; update chars-to-eol, maybe set z
  681.     pushf                ; || save Z for main loop
  682.     ; ah is still current attribute.  Move CX spaces to the screen.
  683.     mov    al, ' '
  684.     call    in_g_mode        ; | graphics mode
  685.     jnc    f_tab_putc        ; |
  686.     REP    STOSW
  687.     popf                ; || restore Z flag for main loop test
  688.     pop    cx            ; restore cx
  689.     jmp    f_looploop        ; Let main loop decrement cx.
  690.  
  691. ;--------------- graphics mode support -----------------------
  692.  
  693. f_tab_putc:    ; graphics mode- call putc to put the char
  694.     add    dx, cx            ; move back to start of tab
  695. f_tp_lp:
  696.     call    putchar
  697.     dec    dx            ; go to next cursor position
  698.     loop    f_tp_lp
  699.     popf                ; Z set if wrapped around EOL
  700.     pop    cx
  701.     jmp    f_looploop
  702.  
  703. ;---- in_g_mode -------------
  704. ; Returns Carry set if not in a graphics mode.
  705. ; Preserves all registers.
  706.  
  707. in_g_mode    proc    near
  708.     cmp    cs:video_mode, 4
  709.     jb    igm_stc
  710.     cmp    cs:video_mode, 7
  711.     jz    igm_stc
  712.     clc
  713.     ret
  714. igm_stc:
  715.     stc
  716.     ret
  717. in_g_mode    endp
  718.  
  719. ;---- Where to go when a character turns out not to be special
  720. f_nctl:
  721. f_not_ansi:
  722.     call    in_g_mode
  723.     jnc    f_g_nctl        ; graphics mode
  724. f_jmptnctl:
  725.     jmp    f_t_nctl        ; text mode
  726.  
  727. ;---- Alternate main loop for graphics mode ----
  728. f_g_cloop:
  729.     LODSB                ; get char! (al = ds:[si++])
  730.     cmp    al, 28            ; is it a control char?
  731.     jb    f_g_control        ;  maybe...
  732. f_g_nctl:
  733.     seg_cs
  734.     xlat
  735.     call    putchar
  736.     dec    dx            ; count down to end of line
  737.     loopnz    f_g_cloop        ; and go back for more.
  738.     jz    f_g_at_eol        ; at end of line; maybe do a crlf.
  739.     jmp    f_loopdone
  740.  
  741. f_g_control:    jmp    f_control
  742. f_g_at_eol:    jmp    f_at_eol
  743.  
  744. ;---- putchar ------------------------------------------------
  745. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  746. ; On entry, registers set up as per xy_to_regs.
  747. ; Preserves all registers.
  748. putchar proc    near
  749.     push    dx
  750.     push    cx
  751.     push    bx
  752.     push    ax
  753.     ; 1. Set cursor position.
  754.     mov    al, cs:max_x
  755.     inc    al
  756.     sub    al, dl
  757.     mov    cs:cur_x, al
  758.     mov    dx, cs:cur_coords    ; get X & Y into DX
  759.     xor    bx, bx            ; choose dpy page 0
  760.     mov    ah, 2            ; chose "Set Cursor Position"
  761.     int    10h            ; call ROM BIOS
  762.     ; 2. Write char & attribute.
  763.     mov    cx, 1
  764.     pop    ax            ; get char in AL
  765.     push    ax
  766.     mov    bl, ah            ; attribute in BL
  767.     mov    bh, 0
  768.     mov    ah, 9
  769.     int    10h
  770.     pop    ax
  771.     pop    bx
  772.     pop    cx
  773.     pop    dx
  774.     ret
  775. putchar endp
  776.  
  777. ;---- set_pseudocursor ------------
  778. ; If in graphics mode, set pseudocursor, else set real cursor.
  779. ; Destroys DS!!!!
  780.  
  781. set_pseudocursor    proc    near
  782.     call    in_g_mode
  783.     jnc    pseudocursor
  784. ; old (more portable, but slower) version
  785. ;    mov    dx, cur_coords        ; get X & Y into DX
  786. ;    xor    bx, bx            ; choose dpy page 0
  787. ;    mov    ah, 2            ; chose "Set Cursor Position"
  788. ;    int    10h            ; call ROM BIOS
  789.  
  790.     ; Write directly to 6845 cursor address register.
  791.     mov    bx, di
  792.     shr    bx, 1            ; convert word index to byte index
  793.  
  794.     mov    dx, port_6845
  795.     mov    al, 0eh
  796.     out    dx, al
  797.  
  798.     jmp    $+2
  799.     inc    dx
  800.     mov    al, bh
  801.     out    dx, al
  802.  
  803.     jmp    $+2
  804.     dec    dx
  805.     mov    al, 0fh
  806.     out    dx, al
  807.  
  808.     jmp    $+2
  809.     inc    dx
  810.     mov    al, bl
  811.     out    dx, al
  812.  
  813.     ; Set cursor position in low memory.
  814.     assume    ds:abs40
  815.     mov    ax, abs40
  816.     mov    ds, ax
  817. ; Does anybody ever use anything but page zero?
  818. ;    mov    al,active_page
  819. ;    cbw
  820. ;    add    ax,ax
  821. ;    xchg    bx,ax
  822.     mov    ax, cs:cur_coords
  823.     mov    cursor_posn,ax
  824.     ret
  825.  
  826.     assume    ds:code
  827. set_pseudocursor    endp
  828.  
  829.  
  830. ;---- pseudocursor --------------------------------------------------
  831. ; Writes a color 15 block in XOR at the current cursor location.
  832. ; Preserves DS, ES, BX, CX, DX, SI, DI.
  833. ; Should be disableable- the pseudocursor slows down single-char
  834. ; writes by a factor of three.
  835. pseudocursor    proc    near
  836.     mov    ax, 8f16h    ; xor, color 15, ^V (small block)
  837.     call    putchar
  838.     ret
  839. pseudocursor    endp
  840.  
  841. ;--------------- end of graphics mode support --------------------
  842.  
  843. dosfn8    endp
  844.  
  845. ;--- get_blank_attrib ------------------------------------------------
  846. ; Determine new attribute and character for a new blank region.
  847. ; Use current attribute, just disallow blink and underline.
  848. ; (Pretty strange way to do it.  Might want to disallow rev vid, too.)
  849. ; Returns result in AH, preserves all other registers.
  850. get_blank_attrib    proc    near
  851.     mov    ah, 0
  852.     call    in_g_mode
  853.     jnc    gb_aok            ; if graphics mode, 0 is bkgnd
  854.  
  855.     mov    ah, cs:cur_attrib
  856.     and    ah, 7fh            ; disallow blink
  857.     cmp    cs:video_mode, 7    ; monochrome?
  858.     jnz    gb_aok
  859.         cmp    ah, 1        ; underline?
  860.         jnz    gb_aok
  861.         mov    ah, 7        ; yep- set it to normal.
  862. gb_aok: ret
  863. get_blank_attrib    endp
  864.  
  865.  
  866. ;---- scroll_up ---------------------------------------------------
  867. ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
  868. ; Moves screen up 1 line, fills the last line with blanks.
  869. ; Attribute of blanks is the current attribute sans blink and underline.
  870.  
  871. scroll_up    proc    near
  872.     push    ax
  873.     push    bx
  874.     push    cx
  875.     push    dx
  876.  
  877.     call    get_blank_attrib
  878.     mov    bh, ah            ; color to use on new blank areas
  879.     mov    al, 1            ; AL is number of lines to scroll.
  880.     mov    ah, 6            ; BIOS: scroll up
  881.     mov    cl, 0            ; upper-left-x of data to scroll
  882.     mov    ch, 0            ; upper-left-y of data to scroll
  883.     mov    dl, cs:max_x        ; lower-rite-x
  884.     mov    dh, cs:max_y        ; lower-rite-y (zero based)
  885.     int    10h            ; call BIOS to scroll a rectangle.
  886.  
  887.     pop    dx
  888.     pop    cx
  889.     pop    bx
  890.     pop    ax
  891.     ret
  892. scroll_up    endp
  893.  
  894. ;---- lookup -----------------------------------------------
  895. ; Called by getchar, peekchar, and key to see if a given key has
  896. ; been redefined.
  897. ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
  898. ; Returns with Z cleared if no redefinition; otherwise,
  899. ; Z is set, SI points to redefinition string, CX is its length.
  900. ; Preseves AL, all but CX and SI.
  901. ; Redefinition table organization:
  902. ;  Strings are stored in reversed order, first char last.
  903. ;  The word following the string is the character to be replaced;
  904. ;  the next word is the length of the string sans header.
  905. ; param_end points to the last byte used by the parameter buffer;
  906. ; redef_end points to the last word used by the redef table.
  907.  
  908. lookup    proc    near
  909.     mov    si, redef_end        ; Start at end of table, move down.
  910.     or    al, al
  911.     jz    lu_lp
  912.     mov    ah, 0            ; clear extraneous scan code
  913. lu_lp:    cmp    si, param_end
  914.     jbe    lu_notfound        ; If below redef table, exit.
  915.     mov    cx, [si]
  916.     cmp    ax, [si-2]        ; are you my mommy?
  917.     jz    lu_gotit
  918.     sub    si, 4
  919.     sub    si, cx            ; point to next header
  920.     jmp    lu_lp
  921. lu_notfound:
  922.     or    si, si            ; clear Z
  923.     jmp    short lu_exit
  924. lu_gotit:
  925.     sub    si, 2
  926.     sub    si, cx            ; point to lowest char in memory
  927.     cmp    al, al            ; set Z
  928. lu_exit:
  929.     ret
  930. lookup    endp
  931.  
  932. ;---- searchbuf --------------------------------------------
  933. ; Called by getchar and peekchar to see if any characters are
  934. ; waiting to be gotten from sources other than BIOS.
  935. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  936. searchbuf    proc    near
  937.     ; Search the stuffahead buffers.
  938.     mov    cx, 4            ; number of buffers to check for chars
  939.     mov    bx, offset fnkey - 4
  940. sbloop: add    bx, 4            ; point to next buffer record
  941.     mov    si, [bx].len
  942.     or    si, si            ; empty?
  943.     loopz    sbloop            ; if so, loop.
  944.     ret
  945. searchbuf    endp
  946.  
  947. ;---- getchar -----------------------------------------------
  948. ; Returns AL = next char.
  949. ; Trashes AX, BX, CX, BP, SI.
  950. getchar proc    near
  951. gc_searchbuf:
  952.     ; See if any chars are waiting in stuffahead buffers.
  953.     call    searchbuf
  954.     jz    gc_trykbd        ; No chars?  Try the keyboard.
  955.     ; A nonempty buffer was found.
  956.     dec    [bx].len
  957.     dec    si
  958.     mov    bp, [bx].adr        ; get pointer to string
  959.     mov    al, byte ptr ds:[bp][si]; get the char
  960.     ; Recognize function key sequences, move them to highest priority
  961.     ; queue.
  962.     sub    si, 1            ; set carry if si=0
  963.     jc    gc_nofnkey        ; no chars left -> nothing to protect.
  964.     cmp    bx, offset fnkey
  965.     jz    gc_nofnkey        ; already highest priority -> done.
  966.     or    al, al
  967.     jnz    gc_nofnkey        ; nonzero first byte -> not fnkey.
  968.     ; Found a function key; move it to highest priority queue.
  969.     dec    [bx].len
  970.     mov    ah, byte ptr ds:[bp][si]; get the second byte of fn key code
  971. gc_fnkey:
  972.     mov    fnkey.len, 1
  973.     mov    fnkeybuf, ah        ; save it.
  974. gc_nofnkey:
  975.     ; Valid char in AL.  Return with it.
  976.     jmp    short gcdone
  977.  
  978. gc_trykbd:
  979.     ; Actually get a character from the keyboard.
  980.     mov    ah, 0
  981.     int    16h            ; BIOS returns with char in AX
  982.     ; If it's Ctrl-break, it has already been taken care of.
  983.     or    ax, ax
  984.     jz    gc_trykbd
  985.  
  986.     ; Look in the reassignment table to see if it needs translation.
  987.     call    lookup            ; Z=found; CX=length; SI=ptr
  988.     jnz    gc_noredef
  989.     ; Okay; set up the reassignment, and run thru the translation code.
  990.     mov    xlatseq.len, cx
  991.     mov    xlatseq.adr, si
  992.     jmp    gc_searchbuf
  993. gc_noredef:
  994.     ; Is it a function key?
  995.     cmp    al, 0
  996.     jz    gc_fnkey        ; yep- special treatment.
  997. gcdone: ret    ; with character in AL.
  998.  
  999. getchar endp
  1000.  
  1001. ;---- peekchar -----------------------------------------------
  1002. ; Returns Z if no character ready, AL=char otherwise.
  1003. ; Trashes AX, BX, CX, BP, SI.
  1004. peekchar    proc    near
  1005. pc_searchbuf:
  1006.     call    searchbuf
  1007.     jz    pc_trykbd        ; No chars?  Try the keyboard.
  1008.     ; A nonempty buffer was found.
  1009.     dec    si
  1010.     mov    bp, [bx].adr        ; get pointer to string
  1011.     mov    al, byte ptr ds:[bp][si]; get the char
  1012.     ; Valid char from buffer in AL.  Return with it.
  1013.     jmp    short pcdone
  1014. pc_trykbd:
  1015.     ; Actually peek at the keyboard.
  1016.     mov    ah, 1
  1017.     int    16h            ; BIOS returns with char in AX
  1018.     jz    pcexit
  1019.     ; If it's control-break, it's already been taken care of.
  1020.     or    ax, ax
  1021.     jnz    pc_notbrk
  1022.     mov    ah, 0
  1023.     int    16h            ; so get rid of it!
  1024.     jmp    short pc_trykbd
  1025. pc_notbrk:
  1026.     ; Look in the reassignment table to see if it needs translation.
  1027.     call    lookup            ; Z=found; CX=length; SI=ptr
  1028.     jnz    pcdone            ; Nope; just return the char.
  1029.     ; Okay; get the first code to be returned.
  1030.     add    si, cx
  1031.     mov    al, [si-1]
  1032. pcdone: or    ah, 1            ; NZ; char ready!
  1033. pcexit: ret    ; with character in AL, Z true if no char waiting.
  1034. peekchar    endp
  1035.  
  1036. ;---- beep ------------------------------------------------------
  1037. ; Beep speaker; period given by beep_div, duration by beep_len.
  1038. ; Preserves all registers.
  1039.  
  1040. beep_div    dw    1300        ; fairly close to IBM beep
  1041. beep_len    dw    3        ; 3/18 sec- shorter than IBM
  1042.  
  1043. beep    proc    near
  1044.     push_all
  1045.  
  1046.     mov    al, 10110110b        ; select 8253
  1047.     mov    dx, 43h            ; control port address
  1048.     out    dx, al
  1049.     dec    dx            ; timer 2 address
  1050.     mov    ax, cs:beep_div
  1051.     jmp    $+2
  1052.     out    dx, al            ; low byte of divisor
  1053.     xchg    ah, al
  1054.     jmp    $+2
  1055.     out    dx, al            ; high byte of divisor
  1056.     mov    dx, 61h
  1057.     jmp    $+2
  1058.     in    al, dx            ; get current value of control bits
  1059.     push    ax
  1060.     or    al, 3
  1061.     jmp    $+2
  1062.     out    dx, al            ; turn speaker on
  1063.  
  1064.     ; Wait for desired duration by monitoring time-of-day 18 Hz clock
  1065.     push    es
  1066.     mov    ax, abs40
  1067.     mov    es, ax
  1068.     assume    es:abs40
  1069.     mov    bx, timer_low
  1070.     add    bx, cs:beep_len
  1071.     mov    cx, -1            ; emergency, in case clock dead
  1072. beeplp: mov    ax, timer_low
  1073.     cmp    ax, bx
  1074.     jg    beepover
  1075.     loop    beeplp
  1076. beepover:
  1077.     pop    es
  1078.     assume    es:code
  1079.  
  1080.     ; Turn off speaker
  1081.     pop    ax
  1082.     and    al, not 3        ; turn speaker off
  1083.     out    dx, al
  1084.     pop_all
  1085.     ret
  1086. beep    endp
  1087.  
  1088. CODE    ends
  1089.  
  1090.     end                ; of nansi.asm
  1091.     page    66, 132
  1092.